from sqlalchemy.schema import Table

from LSTM.variableset import EMVar
from emmodels.em_basemodel import BaseModel
from emmongo import  em_mongomanager as mongoManager
from emmysql.em_sqlmanager import metaData
from emutils import time_utils


class BaseTradeHis(object):
    _db_mapper = {
        'trade_day': 'trade_day',
        'trade_time': 'trade_time',
        'stk_code': 'stk_code',
        'stk_name': 'stk_name',
        'trade_type': 'trade_type',
        'trade_count': 'trade_count',
        'total_money': 'total_money',
        'trade_price': 'trade_price',
        'trade_source': 'trade_source',
    }
    # _table = Table('EM_BASETRADEHIS', metaData, autoload=True)
    # BaseModel.loadTable('EM_BASETRADEHIS')

    def __init__(self, trade_day, trade_time, stk_code, stk_name, trade_type, trade_price, trade_count, total_money,
                 operationDetail, trade_source, yongjin):
        self.trade_day = trade_day
        self.trade_time = trade_time
        self.stk_code = stk_code
        self.stk_name = stk_name
        self.trade_type = trade_type
        self.trade_price = trade_price
        self.trade_count = abs(trade_count)
        self.total_money = total_money
        self.trade_source = trade_source
        self.operationDetail = operationDetail
        self.yongjin = yongjin



    def __str__(self):
        return '%s:%s以%f%s%s(%s)%i股' % (self.operationDetail, self.trade_time,
                                        self.trade_price, self.trade_type,
                                        self.stk_code, self.stk_name, self.trade_count)

    def validate(self):
        if (self.operationDetail == '证券买入' or self.operationDetail == '证券卖出') and self.stk_name != '报价标准券':
            return True
        return False

class MyOwnStock(BaseTradeHis):



    def __init__(self, owner_strategy='AI-' ,stk_code=None, stk_name=None, record=None):

        self.owner_strategy= owner_strategy
        self.stk_code = stk_code
        self.stk_name = stk_name

        self.cur_total_count = 0 #持仓总数
        self.cur_valid_count = 0 #可用持仓
        self.cur_invalid_count = 0 #冻结持仓

        self.total_money = 0 #持仓的市值，成本价 * 持有数
        self.trade_price = 0 # 成本价
        self.buy_price = 0

        self.cur_price = 0 #最新价格
        self.cur_total_money = 0 #最新市场市值
        self.yongjin = 0


        # 需要每次操作往里面添加
        self.trade_source = []
        self.operationDetail = []
        # 下面这些字段只能在buy里面更新，因为判断是否可用持仓数目的时候要跟交易时间做比较，这个交易时间是这个票子最后的买入时间
        self.first_buy_day = None
        self.last_buy_day = None
        self.last_buy_time = None
        # 当前最新的交易日，用来检查是否可以卖，持仓多少天之类
        self.cur_date = None


        self.stk_keep_days = 0
        self.cur_profit = 0
        self.high_profit = 0
        self.low_profit = 0
        self.reback = 0

        self.state = None
        if record :
            self.handle_record(record)

    def _is_correct_record(self, hisrecord):
        if not isinstance(hisrecord, BaseTradeHis):
            raise Exception('不是交易类别')
        if hisrecord.trade_type not in ['买入','持仓','卖出']:
            raise Exception('非法操作!!')
        return True

    def handle_record(self, record):
        if self._is_correct_record(record):
            if record.trade_type == '':
                pass
            elif record.trade_type == '':
                pass

    def add_buy_record(self, record):
        self.stk_code = record.stk_code
        self.stk_name = record.stk_name

        self.cur_total_count += record.trade_count  # 持仓总数
        self.cur_invalid_count += record.trade_count  # 冻结持仓

        self.total_money += record.total_money  # 持仓的市值，成本价 * 持有数

        if not self.first_buy_day:
            self.first_buy_day = record.trade_day
        self.last_buy_day = record.trade_day
        self.last_buy_time = record.trade_time
        self.state = '持仓'

        if self.cur_total_count == record.trade_count:
            # 如果是当前持仓跟买入的一样，说明之前已经清仓或者一直空仓，需要加上之前的成本偏移，比如之前成本是-1元，买入价格12，现在就是11的成本
            self.trade_price += record.trade_price
        else:
            # 如果当前持仓跟买入的记录不一样，说明之前有剩余的持仓
            self.trade_price = self.total_money / self.cur_total_count
        # 保存起来成本价
        self.buy_price = self.trade_price
        self.yongjin += record.yongjin
        self._update_with_record(record)



    # 类似堆栈的形式
    def pop_saile_record(self, record):
        # 卖之前首先还是要更新一下当前的持仓情况
        self.update_own_stk(cur_price=record.trade_price, cur_date=record.trade_day)
        # 判断可用数据是否大于卖出的数量
        if record.trade_count > self.cur_valid_count:
            raise Exception('卖出数量大于可用数量')

        # 更新当前持仓数量
        self.cur_total_count -= record.trade_count  # 持仓总数
        self.cur_valid_count -= record.trade_count  # 可用持仓数
        # 卖出，更新当前总市值
        self.total_money -= record.total_money
        self.yongjin += record.yongjin
        # 更新成本,原本10元 1000股 9元卖100股 还剩900股 成本价为 x : 900x+900=10000 ==》 (10000-900) / 900
        # 更新持仓成本，
        # 卖出数量小于持有数量: 持仓成本永远都是当前持仓的总价值/当前的持仓总数
        # 卖出数量等于持有数量: 持仓成本等于 之前的持仓成本 - 当前的卖出价格，为后面买入的时候作为一个基准
        if self.cur_total_count == 0:
            # 全部卖出
            self.trade_price -= record.trade_price
            self.state = '清仓'
        else:
            self.trade_price = self.total_money / self.cur_total_count
        self._update_with_record(record)

        pass

    def _update_with_record(self, record):

        self.cur_price = record.trade_price  # 最新价格
        self.cur_total_money = self.cur_price * self.cur_total_count  # 最新市场市值

        self.trade_source.append(record.trade_source)
        self.operationDetail.append(record.operationDetail)

        self.cur_date = record.trade_day



    # 持仓多少天, 默认从第一次买入的那一天开始算
    def keep_stk_days(self, cur_date, since_first=True):
        if since_first:
            keep_days = time_utils.delta_work_days_str(self.first_buy_day, cur_date)
        else:
            keep_days = time_utils.delta_work_days_str(self.last_buy_day, cur_date)
        return int(keep_days)

    # 到达目标价格或者止损
    def is_stop_win(self, target_price=0, target_profit=0):
        return False

    def is_stop_loss(self, target_price=0, target_profit=0):
        return False

    def _is_archive_target(self, target_price=0, target_profit=0):
        return False

    # 根据当前时间更新可用的数，不可用的数，总数等
    def update_count(self, cur_date):
        t = self.cur_invalid_count
        m = self.cur_valid_count
        self.cur_valid_count = 0 # Reset
        self.cur_invalid_count = 0 # Reset
        last_keep_days = self.keep_stk_days(cur_date=cur_date,since_first=False)
        if last_keep_days>0:
            self.cur_valid_count = t+m
            self.cur_invalid_count = 0
        else:
            self.cur_valid_count = m
            self.cur_invalid_count = t
        return self.cur_valid_count, self.cur_invalid_count

    # 更新当前的持仓情况，主要是更新持仓时间，可用持仓数量，最高盈亏，最低盈亏，盈利金额
    def update_own_stk(self,cur_price=0, cur_date=None):
        if cur_price == 0 or not cur_date:
            raise Exception('Price or Count could not be 0!! CurDate Could Not be Empty')
        self.cur_price = cur_price
        self.cur_total_money = self.cur_price * self.cur_total_count
        self.cur_date = cur_date
        self.stk_keep_days = self.keep_stk_days(cur_date=cur_date, since_first=True)
        # 当前浮动盈亏
        self.cur_profit = (self.cur_price - self.trade_price) / self.trade_price
        # 最高盈亏
        self.high_profit = max(self.high_profit, self.cur_profit)
        # 最低盈亏
        self.low_profit = min(self.low_profit, self.cur_profit)
        # 回撤
        self.reback = self.high_profit - self.cur_profit
        # 盈利
        self.cur_profit_money = self.cur_total_money - self.total_money
        # 更新持仓冻结数以及可用数
        self.update_count(cur_date)
        pass

    def __str__(self):
        return 'OwnStock: 持有 %s(%s) %i 股 买入成本%f, 当前价格%f,\n持仓 %d 天(从%s-%s),\n浮动盈亏%f, 最高盈亏%f, 回撤%f, 最低盈亏%f \n' % (
            self.stk_code, self.stk_name,self.cur_total_count, self.trade_price,
            self.cur_price,self.stk_keep_days, self.first_buy_day,self.cur_date,
            self.cur_profit, self.high_profit, self.reback, self.low_profit)


    def update_one(self,**kwargs):
        v = self.__dict__.copy()
        # for key, value in kwargs.items():
        #     # 将postion中的字段标记
        #     v['p_'+key] = value
        mongoManager.db[mongoManager.COL_AI_TRADE_HIS].update_one({'stk_code':self.stk_code, 'owner_strategy':self.owner_strategy},
                            {'$set':v},upsert=True)



